<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="keywords" content="好好学习">
    <meta name="description" content="这是一个学习的第一课">
    <title>JS学习</title>
    <style>
        body,p,h1,h2,h3,h4,h5,h6,ul{margin: 0;padding: 0;}
        body{font:12px"宋体","Arial Narrow",HELVETICA;background:#fff;-webkit-text-size-adjust:100%;}
        li{list-style: none;}
        a{text-decoration: none;}
        img{border: 0;vertical-align: middle;}
        #wrap{
            position: relative;
            margin: 0 auto;
            width: 512px;
            height: 768px;
            user-select: none;
            overflow: hidden;
        }
        #main{
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: url("./images/bg_1.jpg");
        }
        #main h2{
            margin-top: 150px;
            font-size: 40px;
            font-family: "heiti";
            text-align: center;
            color: #fff;
        }
        #main ul{
            margin: 50px auto;
            width: 200px;
        }
        #main ul li{
            margin: 30px 0;
            width: 100%;
            height: 50px;
            font-size: 30px;
            line-height: 50px;
            font-family: "heiti";
            background: #ccc;
            text-align: center;
            cursor: pointer;
        }
        #main ul li:hover{
            background: #000;
            color: #fff;
        }
        #gameBox{
            width: 100%;
            height: 100%;
        }
        #gameBox .background{
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 200%;
            z-index: -1;
        }
        #gameBox .score{
            margin: 20px 0 0 20px;
            color: #fff;
            font-size: 20px;
        }
        #over{
            box-sizing: border-box;
            padding: 200px 0;
            width: 100%;
            height: 100%;
            background: url("./images/bg_1.jpg");
        }
        #over p{
            margin: 50px auto 0;
            width: 300px;
            color: #fff;
            font-size: 30px;
            text-align: center;
        }
        #over .restartBtn{
            height: 50px;
            line-height: 50px;
            color: #000;
            background: #ccc;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div id="wrap">
        <div id="main">
            <h2>飞 机 大 战</h2>
            <ul>
                <li>简 单</li>
                <li>困 难</li>
                <li>地 狱</li>
            </ul>
        </div>
        <div id="gameBox">
            <div class="background"></div>
            <span class="score">0</span>
        </div>
        <div id="over">
            <p>游戏结束</p>
            <p>你的得分</p>
            <p class="finalScore">0</p>
            <p class="restartBtn">重新开始</p>
        </div>
    </div>
    <script>
        let wrap = document.querySelector("#wrap"),
            mainBox = document.querySelector("#main"),
            levelBtn = document.querySelectorAll("#main li"),
            gameBox = document.querySelector("#gameBox"),
            nowScore = document.querySelector(".score"),
            bgBox = document.querySelector("#gameBox .background"),
            overBox = document.querySelector("#over"),
            endScore = document.querySelector(".finalScore"),
            restartBtn = document.querySelector(".restartBtn"),
            bulletSpeed,bulletRate,enemyRate,enemySpeed,creatBigEnemy,bgSrc
            ;
        const gameControl = {
            bulletSpeed : [10, 8, 5],
            bulletRate : [10, 20, 30],
            enemySpeed : [2, 3, 5],
            enemyRate : [30, 20, 10],
            bigEnemy : [5, 4, 3],
            bgSrc : ["./images/bg_2.jpg", "./images/bg_3.jpg", "./images/bg_4.jpg"]
        };

        class Sprite {
            constructor(imgSrc, width, height, x = 0, y = 0) {
                this.img = new Image();
                this.img.src = imgSrc;
                this.img.width = width;
                this.img.height = height;
                this.img.style.position = "absolute";
                this.img.style.top = y + "px";
                this.img.style.left = x + "px";
            }
            initialize() {
                gameBox.appendChild(this.img);
            }
            remove() {
                gameBox.removeChild(this.img);
            }
        }
        class Gamedate {
            reset() {
                this.frame = 0;
                this.score = 0;
                this.bullets = [];
                this.enemys = [];
                this.gameOver = false;
            }
            removeEnemy(ele) {
                let temp = this.enemys.indexOf(ele);
                this.enemys.splice(temp, 1);
            }
            removeBullet(ele) {
                let temp = this.bullets.indexOf(ele);
                this.bullets.splice(temp, 1);
            }
        }
        class Plane extends Sprite {
            constructor(x, y) {
                super("./images/plane_1.png", 50, 50, x - 25, y - 25);
                this.initialize();
                this.initEvent();
            }
            move(x, y) {
                this.img.style.top = y + "px";
                this.img.style.left = x + "px";
            }
            initEvent() {
                if(this) {
                    document.addEventListener("mousemove", function(ev) {
                        let x = ev.pageX - wrap.offsetLeft - this.img.width / 2;
                        let y = ev.pageY - wrap.offsetTop - this.img.height / 2;
                        x = Math.max(0 - this.img.width / 2, x);
                        x = Math.min(x, wrap.clientWidth - this.img.width / 2);
                        y = Math.max(0, y);
                        y = Math.min(y, wrap.clientHeight - this.img.height / 2);
                        this.move(x, y);
                    }.bind(this))
                }
            }
            shoot() {
                let bullet = new Bullet();
                let x = this.img.offsetLeft + this.img.width / 2 - bullet.img.width / 2;
                let y = this.img.offsetTop + this.img.height / 2 - bullet.img.height / 2;
                bullet.init(x, y);
            }
        }

        class Bullet extends Sprite {
            constructor() {
                super("./images/fire.png", 6, 12);
            }
            init(x, y) {
                this.img.style.top = y + "px";
                this.img.style.left = x + "px";
                this.initialize();
                gamedate.bullets.push(this);
            }
            update() {
                this.img.style.top = this.img.offsetTop - bulletSpeed + "px";
                if( this.img.offsetTop < 0) {
                    gamedate.removeBullet(this);
                    this.remove();
                }
            }
        }
        function rdn(start, end) {
            return Math.floor(Math.random() * (end - start) + start)
        }
        function isCollision(enemy, ele) {
            return (
                (ele.img.offsetLeft + ele.img.clientWidth) > enemy.img.offsetLeft && 
                ele.img.offsetLeft < (enemy.img.offsetLeft + enemy.img.clientWidth) &&
                (ele.img.offsetTop +  ele.img.clientHeight) > enemy.img.offsetTop &&
                ele.img.offsetTop < (enemy.img.offsetTop + enemy.img.clientHeight)
            )
        }
        class Enemy extends Sprite {
            constructor() {
                let imgSrc = gamedate.frame % (enemyRate * creatBigEnemy) === 0 ? "./images/enemy_big.png" : "./images/enemy_small.png";
                let width = gamedate.frame % (enemyRate * creatBigEnemy) === 0 ? 130 : 50;
                let height = gamedate.frame % (enemyRate * creatBigEnemy) === 0 ? 100 : 40;
                let hp = gamedate.frame % (enemyRate * creatBigEnemy) === 0 ? 8 : 1;
                let score = gamedate.frame % (enemyRate * creatBigEnemy) === 0 ? 50 : 10; 
                super(imgSrc, width, height);
                this.speed = rdn(1, 4);
                this.hp = hp;
                this.score = score;
                this.init();
            }
            init() {
                this.img.style.left = rdn(0, wrap.clientWidth) - this.img.width / 2 + "px";
                this.img.style.top = 0 + "px";
                this.initialize();
                gamedate.enemys.push(this);
            }
            update() {
                this.img.style.top = this.img.offsetTop + this.speed + enemySpeed + "px";
                if(this.img.offsetTop > wrap.clientHeight) {
                    gamedate.removeEnemy(this);
                    this.remove();
                }
            }
            boom() {
                this.img.src = "./images/boom_big.png";
                this.img.style.transition = ".5s";
                this.img.style.opacity = "0";
                this.img.addEventListener("transitionend", function() {
                    this.remove();
                }.bind(this))
            }
        }
        class Run {
            constructor(x, y) {
                this.plane = new Plane(x, y);
                gamedate.reset();
                this.bindLoop = this.loop.bind(this)
                this.gameInitialize();
                this.loop();
            }
            gameInitialize() {
                mainBox.style.display = "none";
                overBox.style.display = "none";
                gameBox.style.display = "block";
                bgBox.style.background = `url(${bgSrc})`;
                bgBox.style.top = -wrap.clientHeight + "px";
            }
            update() {
                gamedate.enemys.concat(gamedate.bullets).forEach(ele => {
                    ele.update();
                });
                bgBox.style.top = bgBox.offsetTop + 1 + "px";
                if(bgBox.offsetTop % wrap.clientHeight === 0) {
                    bgBox.style.top = -wrap.clientHeight + "px";
                }
                nowScore.innerHTML = gamedate.score;
            }
            checkCollision() {
                gamedate.enemys.forEach(ele => {
                    for(let i = 0; i < gamedate.bullets.length; ) {
                        if(isCollision(ele, gamedate.bullets[i])){
                            ele.hp--
                            if(ele.hp === 0) {
                                gamedate.score += ele.score;
                                ele.boom();
                                gamedate.removeEnemy(ele);
                            }
                            gamedate.bullets[i].remove();
                            gamedate.removeBullet(gamedate.bullets[i]);
                        } else {
                            i++;
                        }
                    }
                    if(isCollision(ele, this.plane)){
                        ele.remove();
                        gamedate.removeEnemy(ele);
                        this.plane.remove();
                        this.gameOver();
                    }
                })
            }
            gameOver() {
                gamedate.enemys.concat(gamedate.bullets).forEach(ele => {
                    ele.remove();
                })
                gamedate.enemys = [];
                gamedate.bullets = [];
                overBox.style.display = "block";
                gameBox.style.display = "none";
                endScore.innerHTML = gamedate.score;
                nowScore.innerHTML = 0;
                gamedate.gameOver = true;
            }
            loop() {
                if(!gamedate.gameOver) {
                    gamedate.frame++;
                    this.update();
                    this.checkCollision();
                    if(gamedate.frame % bulletRate === 0){
                        this.plane.shoot();
                    }
                    if(gamedate.frame % enemyRate === 0){
                        let enemy = new Enemy();
                    }
                    requestAnimationFrame(this.bindLoop)
                } else {
                    this.gameOver();
                }
            }
        }
        class Ui {
            constructor() {
                this.start();
                this.reStart();
            }
            start() {
                [...levelBtn].forEach((ele, index)=>{
                    ele.onclick = function(ev) {
                        bulletSpeed = gameControl.bulletSpeed[index];
                        bulletRate = gameControl.bulletRate[index];
                        enemySpeed = gameControl.enemySpeed[index];
                        enemyRate = gameControl.enemyRate[index];
                        creatBigEnemy = gameControl.bigEnemy[index];
                        bgSrc = gameControl.bgSrc[index];
                        let x = ev.pageX - wrap.offsetLeft;
                        let y = ev.pageY - wrap.offsetTop;
                        let run = new Run(x, y);
                    }
                })
            }
            reStart() {
                restartBtn.onclick = function() {
                    gamedate.reset();
                    overBox.style.display = "none";
                    mainBox.style.display = "block";
                }
            }
        } 
        let gamedate = new Gamedate();
        let ui = new Ui();
    </script>
</body>
</html>